在前面一篇原型与继承的学习笔记中,最后使用基于原型的范式实现了继承。而 ES6 引入的 class
关键字则提供了更加漂亮的语法,与 Java 等语言有点类似。还是以 Rabbit
继承 Animal
为例,使用 class
关键字的写法:
1 | class Animal { |
需要注意的几点:
class
只是语法糖,在语言层面并未引入新的基本单元,从typeof Animal
的结果function
可以看到,本质上依然是函数。- 子类的构造器
constructor
在访问this
之前必须先调用父类构造器,即调用super(...)
。 - 为了更多地复用代码,在子类中可以使用
super.method()
调用父类的方法。 - 声明的成员方法
run
,stop
,hide
都是定义在其原型上。以stop
方法为例,等同于:Animal.prototype = function () {...}
。 - 声明的静态方法则是定义在
class
函数本身上,而非其原型上。 extends
关键字实际上做了以下工作:- 将
Rabbit.prototype
的原型设为Animal.prototype
,使得Rabbit
的实例可以继承Animal
的实例属性 - 将
Rabbit
的原型设为Animal
,使得Rabbit
本身可以继承Animal
的静态属性
- 将
class
内声明的方法之间没有逗号,
。
静态属性与实例属性
在 JavaScript 中,结合 class
关键字,用类似 Java 中面向对象的思想来理解静态属性与实例属性。静态属性是挂载在 class
本身上的,而实例属性是挂载在原型 prototype
上的。我们以内置的 Object
和 Date
这两个对象为例来说明,如下图所示:

从上图可以清晰的看到:Object
和 Date
两者本身是不存在继承关系的,它们只是通过彼此的原型实现继承关系,仅此而已。
Object 的静态属性
Object.name
Object.length
Object.prototype
Object.assign
Object.getOwnPropertyDescriptor
Object.getOwnPropertyDescriptors
Object.getOwnPropertyNames
Object.getOwnPropertySymbols
Object.is
Object.preventExtensions
Object.seal
Object.create
Object.defineProperties
Object.defineProperty
Object.freeze
Object.getPrototypeOf
Object.setPrototypeOf
Object.isExtensible
Object.isFrozen
Object.isSealed
Object.keys
Object.entries
Object.values
Object 的实例属性
constructor
__defineGetter__
__defineSetter__
hasOwnProperty
__lookupGetter__
__lookupSetter__
isPrototypeOf
propertyIsEnumerable
toString
valueOf
__proto__
toLocaleString
Date 的静态属性
Date.length
Date.name
Date.prototype
Date.now
Date.parse
Date.UTC
Date 的实例属性
constructor
toString
toDateString
toTimeString
toISOString
toUTCString
toGMTString
getDate
setDate
getDay
getFullYear
setFullYear
getHours
setHours
getMilliseconds
setMilliseconds
getMinutes
setMinutes
getMonth
setMonth
getSeconds
setSeconds
getTime
setTime
getTimezoneOffset
getUTCDate
setUTCDate
getUTCDay
getUTCFullYear
setUTCFullYear
getUTCHours
setUTCHours
getUTCMilliseconds
setUTCMilliseconds
getUTCMinutes
setUTCMinutes
getUTCMonth
setUTCMonth
getUTCSeconds
setUTCSeconds
valueOf
getYear
setYear
toJSON
toLocaleString
toLocaleDateString
toLocaleTimeString
.Symbol.toPrimitive
.
instanceof
运算符
instanceof
运算符的语法如下:
1 | obj instanceof Class; |
这一运算符的本质其实是判断 Class.prototype
是否在 obj
对象的原型链上。除此之外,对于那些部署了 Symbol.hasInstance
静态方法的类,还要额外考虑。obj instanceof Class
这一算法的工作过程大致如下:
- 如果某个类部署了
Symbol.hasInstance
静态方法,那么直接使用该方法。比如下面这样:
1 | // assume anything that canEat is an animal |
- 如果某个类没有部署
Symbol.hasInstance
静态方法(大多数类都没有部署)。那么开始检查这个类的原型链,看Class.prototype
是否等于原型链上的某个原型,即做如下比较:
1 | obj.__proto__ === Class.prototype |
还是以 Rabbit
继承 Animal
为例,来看看 instanceof
的工作过程:
1 | class Animal {} |
上面的代码中,rabbit
实例的原型链是这样的:
1 | rabbit -> Rabbit.prototpye -> Animal.prototype -> Object.prototype -> null |
rabbit instanceof Animal
执行的操作是在上面的原型链中查找,看 Animal.prototype
是否出现在原型链中,如果是则返回 true
,否则返回 false
。
类型检查
在 JavaScript 中,主要有 3 种方法可以用来做类型检查:
typeof
主要用于检查基本数据类型,返回一个说明了所属类型的字符串。比如:
1 | alert(typeof 1); // number |
{}.toString
用于检查基本数据类型,内置对象,以及部署了Symbol.toStringTag
属性的对象。可以看做是增强版的typeof
。示例:
1 | // 对于基本数据类型 |
instanceof
用于对检查对象数据类型,尤其用于检查对象继承关系。